{% extends 'base.attached.class' %}

{% block content %}
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define N_FEATURES {{ n_features }}
#define N_CLASSES {{ n_classes }}
#define N_VECTORS {{ n_vectors }}
#define N_WEIGHTS {{ n_weights }}
#define N_COEFFS {{ n_coeffs }}
#define N_INTERS {{ n_inters }}
#define KERNEL_TYPE '{{ kernel }}'
#define KERNEL_GAMMA {{ gamma }}
#define KERNEL_COEF {{ coef0 }}
#define KERNEL_DEGREE {{ degree }}

{{ vectors }}
{{ coeffs }}
{{ inters }}
{{ weights }}

int predict (float features[]) {
    int i, j, k, d, l;
    float kernels[N_VECTORS];
    float kernel;
    switch (KERNEL_TYPE) {
        case 'l':
            // <x,x'>
            for (i = 0; i < N_VECTORS; i++) {
                kernel = 0.;
                for (j = 0; j < N_FEATURES; j++) {
                    kernel += vectors[i][j] * features[j];
                }
                kernels[i] = kernel;
            }
            break;
        case 'p':
            // (y<x,x'>+r)^d
            for (i = 0; i < N_VECTORS; i++) {
                kernel = 0.;
                for (j = 0; j < N_FEATURES; j++) {
                    kernel += vectors[i][j] * features[j];
                }
                kernels[i] = pow((KERNEL_GAMMA * kernel) + KERNEL_COEF, KERNEL_DEGREE);
            }
            break;
        case 'r':
            // exp(-y|x-x'|^2)
            for (i = 0; i < N_VECTORS; i++) {
                kernel = 0.;
                for (j = 0; j < N_FEATURES; j++) {
                    kernel += pow(vectors[i][j] - features[j], 2);
                }
                kernels[i] = exp(-KERNEL_GAMMA * kernel);
            }
            break;
        case 's':
            // tanh(y<x,x'>+r)
            for (i = 0; i < N_VECTORS; i++) {
                kernel = 0.;
                for (j = 0; j < N_FEATURES; j++) {
                    kernel += vectors[i][j] * features[j];
                }
                kernels[i] = tanh((KERNEL_GAMMA * kernel) + KERNEL_COEF);
            }
            break;
    }

    int starts[N_WEIGHTS];
    int start;
    for (i = 0; i < N_WEIGHTS; i++) {
        if (i != 0) {
            start = 0;
            for (j = 0; j < i; j++) {
                start += weights[j];
            }
            starts[i] = start;
        } else {
            starts[0] = 0;
        }
    }

    int ends[N_WEIGHTS];
    for (i = 0; i < N_WEIGHTS; i++) {
        ends[i] = weights[i] + starts[i];
    }

    if (N_CLASSES == 2) {

        for (i = 0; i < N_VECTORS; i++) {
            kernels[i] = -kernels[i];
        }

        float decision = 0.;
        for (k = starts[1]; k < ends[1]; k++) {
            decision += kernels[k] * coeffs[0][k];
        }
        for (k = starts[0]; k < ends[0]; k++) {
            decision += kernels[k] * coeffs[0][k];
        }
        decision += inters[0];

        if (decision > 0) {
            return 0;
        }
        return 1;

    }

    float decisions[N_INTERS];
    float tmp;
    for (i = 0, d = 0, l = N_WEIGHTS; i < l; i++) {
        for (j = i + 1; j < l; j++) {
            tmp = 0.;
            for (k = starts[j]; k < ends[j]; k++) {
                tmp += kernels[k] * coeffs[i][k];
            }
            for (k = starts[i]; k < ends[i]; k++) {
                tmp += kernels[k] * coeffs[j - 1][k];
            }
            decisions[d] = tmp + inters[d];
            d = d + 1;
        }
    }

    int votes[N_INTERS];
    for (i = 0, d = 0, l = N_WEIGHTS; i < l; i++) {
        for (j = i + 1; j < l; j++) {
            votes[d] = decisions[d] > 0 ? i : j;
            d = d + 1;
        }
    }

    int amounts[N_CLASSES];
    for (i = 0, l = N_CLASSES; i < l; i++) {
        amounts[i] = 0;
    }
    for (i = 0; i < N_INTERS; i++) {
        amounts[votes[i]] += 1;
    }

    int classVal = -1;
    int classIdx = -1;
    for (i = 0; i < N_CLASSES; i++) {
        if (amounts[i] > classVal) {
            classVal = amounts[i];
            classIdx= i;
        }
    }
    return classIdx;
}

int main(int argc, const char *argv[]) {

    /* Features: */
    float features[argc-1];
    int i;
    for (int i = 1; i < argc; i++) {
        features[i-1] = atof(argv[i]);
    }

    {% if is_test or to_json %}
    /* Get JSON: */
    printf("{\"predict\": %d }", predict(features));
    {% else %}
    /* Get class prediction: */
    printf("Predicted class: #%d\n", predict(features));
    {% endif %}

    return 0;
}
{% endblock %}